package pap.tasks;

import java.util.List;
import java.util.concurrent.LinkedBlockingDeque;

import pap.NamedThreadFactory;
import pap.SynchronizedCounter;
import pap.Vector;
import pap.bodies.BodiesManager;
import pap.bodies.Body;
import pap.utils.Constants;
import pap.utils.UncompatibleDimensionsException;


/*
 * 
 */
public class ForceCalculator extends Task {

	SynchronizedCounter counter;
	LinkedBlockingDeque<Body> completedBodies;
	List<Body> bodiesList;

	public ForceCalculator(int threadNum,
			LinkedBlockingDeque<Body> completedBodies,
			BodiesManager bodiesManager,
			SynchronizedCounter bodiesToProcessCounter, NamedThreadFactory fC) {

		super(threadNum, fC);

		bodiesList = bodiesManager.getBodiesList();
		counter = bodiesToProcessCounter;
		this.completedBodies = completedBodies;

	}

	@Override
	protected void mainTask() throws UncompatibleDimensionsException,
			InterruptedException {

		calculateForcesOnBody(getNextIndex());

	}

	private int getNextIndex() throws InterruptedException {
		return counter.getNextVal();
	}

	/**
	 * Calculates for the body with index bodyIndex the attraction's force with
	 * other bodies.
	 * 
	 * @param bodyIndex
	 * @throws UncompatibleDimensionsException
	 */
	private void calculateForcesOnBody(int bodyIndex)
			throws UncompatibleDimensionsException {
		Body body = bodiesList.get(bodyIndex);
		Vector force = null;
		for (int i = bodyIndex + 1; i < bodiesList.size(); i++) {

			force = forceBetweenBodies(body, bodiesList.get(i));
			body.addForce(force.reverse());
			bodiesList.get(i).addForce(force);
		}

		completedBodies.addLast(body);
	}

	/**
	 * Calculates the attraction's force applied from a body to another.
	 * 
	 * @param from
	 * @param to
	 * @return
	 * @throws UncompatibleDimensionsException
	 */
	private Vector forceBetweenBodies(Body from, Body to)
			throws UncompatibleDimensionsException {

		// Obtaining the Vector connecting the center of the bodies
		Vector connector = Vector.vectorConnectingPoints(from.getMassCenter(),
				to.getMassCenter());

		// The vector's magnitude is the distance between bodies
		double distance = connector.getMagnitude();

		// Obtaining the unit vector representing the direction of the force
		Vector direction = connector.getDirection();
		double intensity;
		if (distance == 0)
			intensity = Integer.MAX_VALUE;
		else
			// Calculate the force's intensity
			intensity = Constants.G * from.getMass() * to.getMass()
					/ Math.pow(distance, 2);

		return direction.multiply(intensity);
	}

}